﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Description;
using System.Xml.Linq;
using System.Configuration;
using System.ServiceModel;
using System.ServiceModel.Discovery;
using System.ServiceModel.Channels;
using Devtm.ServiceModel.Discovery;
using System.IO;
using System.Xml;

namespace Devtm.ServiceModel.Server
{
    

    [AttributeUsage(AttributeTargets.Class)]
    public class Discovery2Attribute : Attribute, IServiceBehavior
    {

        private const string addressFinale = "[[adressFinale]]";

        public void AddBindingParameters(
            ServiceDescription serviceDescription,
            ServiceHostBase serviceHostBase,
            System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,
            BindingParameterCollection bindingParameters)
        {

        }


        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {

            var endpoints = serviceDescription.Endpoints;
   
            string ConfigurationName = serviceDescription.ConfigurationName;
            string bindingName = string.Empty;
            string bindingConfigurationName = string.Empty;




            foreach (ServiceEndpoint endpoint in endpoints)
                if (endpoint.Contract.ContractType != typeof(IMetadataExchange))
                {


                    WsdlExporter exporter = new WsdlExporter() { PolicyVersion = PolicyVersion.Policy15 };
                    exporter.ExportEndpoint(endpoint);

                    // If there are no errors, get the documents.
                    MetadataSet metadataDocs = null;
                    if (exporter.Errors.Count == 0)
                    {

                        metadataDocs = exporter.GetGeneratedMetadata();

                        string txt = string.Empty;
                        // Serialize
                        using (StringWriter sWriter = new StringWriter())
                        {
                            XmlWriter writer = XmlWriter.Create(sWriter);
                            metadataDocs.WriteTo(writer);
                            writer.Flush();
                            txt = sWriter.ToString();
                        }


                        // new behaviors
                        var endpointDiscoveryBehavior = new EndpointDiscoveryBehavior();

                        // add the extension
                        endpoint.Behaviors.Add(endpointDiscoveryBehavior);

                        Extension configuration = Extension.GetMeta();
                        configuration.Data = new XElement("datas", new XCData(txt));

                        XElement meta = new XElement("metadata", new XAttribute("mode", "Policy15"), Extension.Serialize(configuration));

                        // add the binding information to the endpoint
                        endpointDiscoveryBehavior.Extensions.Add(meta);

                    }


                    // le service implemente INotifyDirectoryChanged
                    //if (endpoint.Contract.ContractType == typeof(INotifyDirectoryChanged))
                    //{
                        //IL demande à etre appelé quand l'annuaire change
                        //configuration.NotifyCriterias.Contract.Namespace = NotifyContext.Namespace;
                        //configuration.NotifyCriterias.Contract.Name = NotifyContext.Name;
                    //}


                    

                }

        }



        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {

        }



        #region private

        private Extension RebuildConfiguration(XElement endPointConf, XElement bindingConf, XElement endPointBehavior, XElement ConfigServiceBehaviors)
        {

            XElement e =
                new XElement("system.serviceModel",

                    new XElement
                        ("client", ConvertToClientEndpoint(endPointConf, EndpointbannedAttribute))
            );

            if (bindingConf != null)
            {
                var bindingName = endPointConf.Attribute("binding").Value;

                XElement g = new XElement("bindings",
                    new XElement(bindingName,
                            bindingConf
                        ));
                e.Add(g);
            }

            if (endPointBehavior != null || ConfigServiceBehaviors != null)
            {

                XElement f = new XElement("behaviors");
                e.Add(f);

                if (endPointBehavior != null)
                    f.Add(new XElement("endpointBehaviors", endPointBehavior));


                if (ConfigServiceBehaviors != null)
                    f.Add(new XElement("serviceBehaviors", ConfigServiceBehaviors));

            }

            Extension container = Extension.GetService();
            container.Data = e;            

            return container;
        }

        private static List<String> EndpointbannedAttribute = new List<string>() { "bindingNamespace", "listenUriMode", "bindingName", "listenUri" };
        private XElement ConvertToClientEndpoint(XElement endPointConf, List<string> bannedList)
        {

            XElement e = XElement.Parse(endPointConf.ToString());
            e.ReplaceAttributes();

            foreach (XAttribute item in endPointConf.Attributes())
                if (!bannedList.Contains(item.Name.LocalName))
                    e.Add(new XAttribute(item.Name, item.Value));

            return e;

        }

        private static XElement FindBehaviorService(XElement ConfigServices, XElement ConfigServicesBehaviors)
        {

            XElement ConfigServiceBehaviors = null;

            var srvBehaviorConfiguration = ConfigServices.Attribute("behaviorConfiguration");
            if (srvBehaviorConfiguration != null)
            {
                var name = srvBehaviorConfiguration.Value;
                foreach (XElement item in ConfigServicesBehaviors.Elements())
                {
                    var name2 = item.Attribute("name");
                    if (name2 != null)
                        if (name2.Value == name)
                        {
                            ConfigServiceBehaviors = item;
                            break;
                        }
                }
            }

            return ConfigServiceBehaviors;
        }

        private XElement FindendPointBehavior(XElement ConfigEndpointBehaviors, XElement endpoint)
        {

            var name1 = endpoint.Attribute("behaviorConfiguration");
            if (name1 == null)
                return null;

            foreach (XElement i in ConfigEndpointBehaviors.Elements())
            {
                var name2 = i.Attribute("name");
                if (name1.Value == name2.Value)
                    return i;
            }

            return null;
        }

        private static void LoadBehaviors(System.Configuration.Configuration config, ref XElement ConfigEndpointBehaviors, ref XElement ConfigServiceBehaviors)
        {
            System.ServiceModel.Configuration.BehaviorsSection behaviors = config.GetSection("system.serviceModel/behaviors") as System.ServiceModel.Configuration.BehaviorsSection;
            var behaviorsConfig = behaviors.SectionInformation.GetRawXml();
            if (!string.IsNullOrEmpty(behaviorsConfig))
            {
                XElement x = XElement.Parse(behaviorsConfig);
                foreach (XElement item in x.Elements())
                {
                    if (item.Name.LocalName == "endpointBehaviors")
                        ConfigEndpointBehaviors = item;
                    else if (item.Name.LocalName == "serviceBehaviors")
                        ConfigServiceBehaviors = item;
                }
            }
        }

        private static XElement FindbindingConfiguration(XElement Configbindings, ServiceEndpoint endpoint, XElement endPointConf)
        {

            XElement bindingConf = null;

            var name1 = endpoint.Binding.Name.ToLower();
            var name2 = endPointConf.Attribute("bindingConfiguration");

            if (name1 != null && Configbindings != null)
            {
                foreach (XElement item in Configbindings.Elements())
                {

                    if (item.Name.LocalName.ToLower() == name1)
                    {

                        foreach (XElement item1 in item.Elements())
                        {

                            var name = item1.Attribute("name").Value;
                            if (name == name2.Value)
                            {
                                bindingConf = item1;
                                //bindingName = item.Attribute("binding").Value;
                                //bindingConfigurationName = item.Attribute("bindingConfiguration").Value;
                                break;
                            }
                        }

                    }
                }
            }
            return bindingConf;
        }

        private static XElement LoadBindings(System.Configuration.Configuration config)
        {

            XElement bindingConfig = null;

            // On charge les bindings             
            System.ServiceModel.Configuration.BindingsSection bindings = config.GetSection("system.serviceModel/bindings") as System.ServiceModel.Configuration.BindingsSection;

            var conf = bindings.SectionInformation.GetRawXml();
            if (!string.IsNullOrEmpty(conf))
                bindingConfig = XElement.Parse(conf);

            return bindingConfig;
        }

        private static XElement LoadServices(string ConfigurationName, System.Configuration.Configuration config)
        {

            XElement ConfigService = null;

            System.ServiceModel.Configuration.ServicesSection services = config.GetSection("system.serviceModel/services") as System.ServiceModel.Configuration.ServicesSection;

            var serviceConfig = XElement.Parse(services.SectionInformation.GetRawXml());

            foreach (XElement item in serviceConfig.Elements())
            {
                var name = item.Attribute("name").Value;
                if (name == ConfigurationName)
                {
                    ConfigService = item;
                    //bindingName = item.Attribute("binding").Value;
                    //bindingConfigurationName = item.Attribute("bindingConfiguration").Value;
                    break;
                }
            }


            return ConfigService;
        }

        private static System.Configuration.Configuration GetConfiguration()
        {

            System.ServiceModel.Configuration.ServicesSection services;

            System.Configuration.Configuration config = null;

            #region Find filename configuration
            services = System.Configuration.ConfigurationManager.GetSection("system.serviceModel/services") as System.ServiceModel.Configuration.ServicesSection;
            if (services.Services.Count == 0)
                throw new MissingConfigurationDescriptionException();
            var source = services.Services[0].ElementInformation.Source;
            if (string.IsNullOrEmpty(source))
                throw new MissingConfigurationDescriptionException();
            #endregion

            // chargement des sections
            var filemap = new ExeConfigurationFileMap() { ExeConfigFilename = source };
            config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(filemap, ConfigurationUserLevel.None);

            return config;
        }

        private static XElement FindEndPointConfiguration(XElement ConfigService, ServiceEndpoint endpoint)
        {

            XElement endP = null;

            string adr = endpoint.Address.Uri.AbsoluteUri;
            foreach (XElement item in ConfigService.Elements())
            {
                var address = item.Attribute("address");
                if (address != null)
                {
                    if (address.Value == adr)
                    {
                        endP = item;
                        break;
                    }
                }
            }
            return endP;
        }
        
        #endregion

        public Type NotifyContext { get; set; }

    }

}

